package com.wenlin.myproduction.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;

import com.wenlin.myproduction.R;
import com.wenlin.myproduction.util.UI;

/**
 * @author 文琳
 * @time 2020/9/21 17:21
 * @desc 设置一个数，再次设置一个数时，这两个数是有差值的，每个位置，个位、十位、百位，可能有数字也可能是空的。如第一个数是99，第二个数字是100，那么第一个数字的百位就应该是空的（可以看做0）
 */
public class GoldView extends View {
    private static final long ANIM_DUR = 1000;
    private int viewWidth, viewHeight;
    private long number, oldNumber;
    private int maxLength, maxChangeLength;
    private String newNumberStr, oldNumberStr;
    private Rect rect;
    private Paint paint;
    private int textSize;
    @ColorInt
    private int textColor;
    private int paddingEnd;//为了避免最后一个数字显示不全，加上一个右侧的内边距
    private int paddingBottom;//为了避免最后一个数字显示不全，加上一个右侧的内边距
    private int paddingWord;//数字之间的间距
    private float distance;//动画：移动的距离
    private ValueAnimator valueAnimator;
    private float wordWidth, wordHeight;
    private boolean newNumberSmaller;//新的数字更小
    private boolean isRelease;//是否释放资源了，如果释放了则不进行绘制等操作了
    private boolean needRequestLayout;//是否需要重新测量
    private boolean keepTopZero;//是否保留0开头的数字的开头的0
    private boolean textBold;

    public GoldView(Context context) {
        this(context, null);
    }

    public GoldView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.GoldView);
        textSize = ta.getDimensionPixelSize(R.styleable.GoldView_gv_text_size, (int) UI.getPx(R.dimen.wpx60));
        textColor = ta.getColor(R.styleable.GoldView_gv_text_color, Color.WHITE);
        textBold = ta.getBoolean(R.styleable.GoldView_gv_text_bold, false);
        paddingEnd = ta.getDimensionPixelSize(R.styleable.GoldView_gv_padding_end, (int) UI.getPx(R.dimen.wpx10));
        paddingBottom = ta.getDimensionPixelSize(R.styleable.GoldView_gv_padding_bottom, (int) UI.getPx(R.dimen.wpx4));
        paddingWord = ta.getDimensionPixelSize(R.styleable.GoldView_gv_padding_word, 0);
        keepTopZero = ta.getBoolean(R.styleable.GoldView_gv_keep_top_zero, false);
        ta.recycle();
        initialize();
    }

    /**
     * 初始化
     */
    private void initialize() {
        paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setDither(true);
        paint.setStrokeWidth(UI.getPx(R.dimen.wpx4));
        paint.setFakeBoldText(textBold);
        paint.setAntiAlias(true);
        paint.setTextSize(textSize);
        paint.setColor(textColor);
        rect = new Rect();

        paint.getTextBounds("4", 0, 1, rect);
        wordWidth = rect.width();
        wordHeight = rect.height();
    }

    /**
     * 设置是否保留0开头的数字的开头的0
     *
     * @param keepTopZero 是否保留
     */
    public void setKeepTopZero(boolean keepTopZero) {
        this.keepTopZero = keepTopZero;
        invalidate();
    }

    /**
     * 设置数值，不要动画
     *
     * @param theNumber 数值
     */
    public void jumpTo(long theNumber) {
        if (isRelease) return;
//        if (theNumber == oldNumber) {//为了执行“000123”这样的数字显示成“123”，所有这里不能直接return，需要继续往下执行
//            return;//等于原数，无需执行
//        }
        number = theNumber;
        if (number < 0) number = 0;
        computeMaxChangeLength();
        newNumberSmaller = number < oldNumber;
        oldNumber = number;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
        needRequestLayout = true;//必须重新计算长度
        invalidate();
    }

    /**
     * 设置数值，并播放动画
     *
     * @param theNumber 数值
     */
    public void runTo(long theNumber) {
        if (isRelease) return;
        if (theNumber == oldNumber) {
            return;//等于原数，无需执行
        }
        number = theNumber;
        if (number < 0) number = 0;
        computeMaxChangeLength();
        newNumberSmaller = number < oldNumber;
        oldNumber = number;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
        startAnimation();
        invalidate();
    }

    /**
     * 从某个数开始跑往另一个数
     *
     * @param number    目标数字
     * @param oldNumber 旧数字
     */
    public void runToFrom(long number, long oldNumber) {
        this.oldNumber = oldNumber;
        runTo(number);
    }

    /**
     * 返回数字
     *
     * @return 所求
     */
    public long getNumber() {
        return number;
    }

    /**
     * 获取每个数位改变次数的最大值。比如099 -> 105，则改动次数分别为1(0~1：0、1)，1(9~0：9、0),6(9~5：9、0、1、2、3、4、5)
     */
    private void computeMaxChangeLength() {
        needRequestLayout = newNumberStr != null && newNumberStr.startsWith("0") || String.valueOf(number).length() != String.valueOf(oldNumber).length();
        StringBuilder oldSB = new StringBuilder(String.valueOf(oldNumber));
        StringBuilder newSB = new StringBuilder(String.valueOf(number));
        int oldLength = oldSB.length();
        int newLength = newSB.length();
        maxLength = Math.max(oldLength, newLength);
        //新旧两个数通过前面加0的方式，凑成两个一样长的字符串
        if (oldLength < maxLength) {
            for (int i = oldLength; i < maxLength; i++) {
                oldSB.insert(0, "0");
            }
        } else if (newLength < maxLength) {
            for (int i = newLength; i < maxLength; i++) {
                newSB.insert(0, "0");
            }
        }
        oldNumberStr = oldSB.toString();
        newNumberStr = newSB.toString();
        maxChangeLength = 0;//新旧两个数字的相应数位的改变次数之中的最大值
        int oldNumberTemp;
        int newNumberTemp;
        int delta;
        for (int i = 0; i < maxLength; i++) {
            oldNumberTemp = Integer.parseInt(getStrIndexOf(i, oldNumberStr));
            newNumberTemp = Integer.parseInt(getStrIndexOf(i, newNumberStr));
            if (newNumberSmaller) {
                if (oldNumberTemp < newNumberTemp) oldNumberTemp += 10;
                delta = oldNumberTemp - newNumberTemp;
            } else {
                if (newNumberTemp < oldNumberTemp) newNumberTemp += 10;
                delta = newNumberTemp - oldNumberTemp;
            }
            maxChangeLength = Math.max(delta, maxChangeLength);
        }
    }

    /**
     * 开始动画
     */
    private void startAnimation() {
        if (isRelease) return;
        valueAnimator = ValueAnimator.ofFloat(0, wordHeight * maxChangeLength);
        valueAnimator.addUpdateListener(animation -> {
            distance = (float) animation.getAnimatedValue();
            invalidate();
        });
        valueAnimator.setDuration(ANIM_DUR);
        valueAnimator.start();
    }

    /**
     * 释放资源
     */
    public void release() {
        isRelease = true;
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllUpdateListeners();
            valueAnimator.removeAllListeners();
            valueAnimator = null;
        }
    }

    /**
     * 获取指定位置的字符
     *
     * @param index 位置脚标
     * @param str   字符串
     * @return 所求
     */
    private String getStrIndexOf(int index, String str) {
        if (str != null && str.length() > index) {
            return str.substring(index, index + 1);
        }
        return "";
    }

    /**
     * 获取单个数字，如果原数大于10，则取个位数字，如果小于0，则取加10之后的数字
     *
     * @param n 原数
     * @return 所求
     */
    private int getSingleFormatNumber(int n) {
        if (n >= 10) n -= 10;
        if (n < 0) n += 10;
        return n;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isRelease) return;
        if (needRequestLayout) {
            requestLayout();
            needRequestLayout = false;
        }
        for (int i = 0; i < maxLength; i++) {//这里的i表示数位（个位、十位）
            String oldOne = getStrIndexOf(i, oldNumberStr);
            String newOne = getStrIndexOf(i, newNumberStr);
            if (valueAnimator != null && valueAnimator.isRunning()) {
                if (newOne.equals(oldOne)) {//没有变动的数位保持不动
                    canvas.drawText(newOne, i * (wordWidth + paddingWord), wordHeight, paint);
                } else {//有变动的数位开始动画
                    int oldN = Integer.parseInt(oldOne);
                    int newN = Integer.parseInt(newOne);
                    int delta;//各数位上变动前后的数字滑动步数：0向上滑动到9需9步。0若向下滑动到9只需1步
                    if (newNumberSmaller) {
                        if (oldN < newN) oldN += 10;
                        delta = oldN - newN;
                    } else {
                        if (newN < oldN) newN += 10;
                        delta = newN - oldN;
                    }
                    for (int j = 0; j < delta; j++) {
                        float y1, y2;
                        if (newNumberSmaller) {//如果新数更小
                            y1 = (1 - j) * wordHeight + (float) delta / (float) maxChangeLength * distance;
                            y2 = y1 - wordHeight;
                            if (y1 > 0 && y1 < wordHeight * 2)
                                canvas.drawText(String.valueOf(getSingleFormatNumber(oldN - j)), i * (wordWidth + paddingWord), y1, paint);
                            if (y2 > 0 && y2 < wordHeight * 2)
                                canvas.drawText(String.valueOf(getSingleFormatNumber(oldN - j - 1)), i * (wordWidth + paddingWord), y2, paint);
                        } else {//如果新数更大
                            y1 = (j + 1) * wordHeight - (float) delta / (float) maxChangeLength * distance;
                            y2 = y1 + wordHeight;
                            if (y1 > 0 && y1 < wordHeight * 2)
                                canvas.drawText(String.valueOf(getSingleFormatNumber(oldN + j)), i * (wordWidth + paddingWord), y1, paint);
                            if (y2 > 0 && y2 < wordHeight * 2)
                                canvas.drawText(String.valueOf(getSingleFormatNumber(oldN + j + 1)), i * (wordWidth + paddingWord), y2, paint);
                        }
                    }
                }
            } else {//没有执行动画的时候，比如使用jumpTo()，直接显示数字就完了
                canvas.drawText(newOne, i * (wordWidth + paddingWord), wordHeight, paint);
            }
        }
        //解决runTo一个以零开头的数字时，零依然显示的问题
        if (!keepTopZero && !(valueAnimator != null && valueAnimator.isRunning())
                && newNumberStr != null && newNumberStr.startsWith("0"))
            jumpTo(number);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        paint.getTextBounds("4", 0, 1, rect);
        wordWidth = rect.width();
        wordHeight = rect.height();
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    /**
     * 获取宽或者高
     *
     * @param measureSpec 测量单位
     * @param isWidSpec   是宽还是高
     * @return 所求
     */
    private int measure(int measureSpec, boolean isWidSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            viewWidth = (int) Math.ceil((wordWidth + paddingWord) * maxLength) + paddingEnd;//为了避免最后一个数字显示不全，加上一个右侧的内边距
            viewHeight = (int) Math.ceil(wordHeight) + paddingBottom;
            result = isWidSpec ? viewWidth : viewHeight;
            if (mode == MeasureSpec.AT_MOST) result = Math.min(result, size);
        }
        return result;
    }
}
